package cn.demoncat.util.mybatis.plus;

import cn.demoncat.util.lang.ArrayUtil;
import cn.demoncat.util.lang.CollectionUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.constant.StringConstant;
import cn.demoncat.util.mybatis.param.CreateTime;
import cn.demoncat.util.mybatis.param.UpdateTime;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.enums.SqlLike;
import org.apache.commons.lang3.ArrayUtils;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

import static com.baomidou.mybatisplus.core.enums.SqlKeyword.EQ;
import static com.baomidou.mybatisplus.core.enums.SqlKeyword.NE;
import static com.baomidou.mybatisplus.core.enums.WrapperKeyword.APPLY;

/**
 * 更新条件
 *
 * 1、如果参数为null，忽略；如果所有参数都为null，SQL语法报错
 *
 * @author 延晓磊
 * @since 2021年08月27日
 */
public class Update<T> extends UpdateWrapper<T> {

    // 是否支持驼峰参数
    private boolean supportHumpParam;


    /**
     * 支持驼峰参数
     *
     * 1、调用本方法后，如果参数为 xxXx ，将转为字段名 xx_xx（调用前的不会转换）
     * 2、如果字段名没有强制 xx_xx，而是允许驼峰，不要调用本方法
     *
     * @return
     */
    public Update<T> hump(){
        this.supportHumpParam = true;
        return this;
    }

    /* ==================== 更新条件 ==================== */

    /**
     * SET
     *
     * @param column
     * @param val       为null时忽略
     * @return
     */
    @Override
    public Update<T> set(String column, Object val) {
        return this.set(true, column, val);
    }

    /**
     * SET
     *
     * @param condition
     * @param column
     * @param val       为null时忽略
     * @return
     */
    @Override
    public Update<T> set(boolean condition, String column, Object val) {
        if (condition && val != null) {
            super.set(true, columnToString(column), val);
        }
        return this;
    }

    /**
     * SET
     *
     * @param column
     * @param val       为null时设置为null
     * @return
     */
    public Update<T> setNull(String column, Object val) {
        return this.setNull(true, column, val);
    }

    /**
     * SET
     *
     * @param condition
     * @param column
     * @param val       为null时设置为null
     * @return
     */
    public Update<T> setNull(boolean condition, String column, Object val) {
        super.set(condition, columnToString(column), val);
        return this;
    }

    /* ==================== 查询条件（拷贝自Query） ==================== */

    /**
     * 空条件
     *
     * @param <T>
     * @return
     */
    public static <T> Update<T> empty() {
        return new Update<>();
    }

    /**
     * eq 条件
     *
     * @param column
     * @param val   为空时忽略，判断空须使用isEmpty或isNullEmpty
     * @return
     */
    @Override
    public Update<T> eq(String column, Object val) {
        return this.eq(true, column, val);
    }

    /**
     * eq 条件
     *
     * @param condition
     * @param column
     * @param val   为空时忽略，判断空须使用isEmpty或isNullEmpty
     * @return
     */
    @Override
    public Update<T> eq(boolean condition, String column, Object val) {
        super.eq(condition, column, val);
        return this;
    }

    /**
     * eq 条件
     *
     * @param columnAndValues   键,值...（为空时忽略，判断空须使用isEmpty或isNullEmpty）
     * @return
     */
    public Update<T> eqs(String ... columnAndValues) {
        if (ArrayUtils.isNotEmpty(columnAndValues)) {
            for (int i = 0; i < columnAndValues.length; i = i+2) {
                this.eq(columnAndValues[i], columnAndValues[i+1]);
            }
        }
        return this;
    }

    /**
     * eq 条件
     *
     * @param params    为空时忽略，判断空须使用isEmpty或isNullEmpty
     * @return
     */
    @Override
    public <V> Update<T> allEq(Map<String, V> params) {
        super.allEq(params, false);
        return this;
    }

    /**
     * ne 条件
     *
     * @param column
     * @param val   为空时忽略，判断空须使用isNotEmpty或isNotNullEmpty
     * @return
     */
    @Override
    public Update<T> ne(String column, Object val) {
        return this.ne(true, column, val);
    }

    /**
     * ne 条件
     *
     * @param condition
     * @param column
     * @param val   为空时忽略，判断空须使用isNotEmpty或isNotNullEmpty
     * @return
     */
    @Override
    public Update<T> ne(boolean condition, String column, Object val) {
        super.ne(condition, column, val);
        return this;
    }

    /**
     * eq 条件
     *
     * @param condition
     * @param column
     * @param sqlKeyword
     * @param val   为空时忽略，判断空须使用super.addCondition()
     * @return
     */
    @Override
    protected Update<T> addCondition(boolean condition, String column, SqlKeyword sqlKeyword, Object val) {
        if (condition && condition(val)) {
            super.addCondition(true, column, sqlKeyword, val);
        }
        return this;
    }

    /**
     * like 条件
     *
     * @param column
     * @param val       为空时忽略
     * @return '%值%'
     */
    @Override
    public Update<T> like(String column, Object val) {
        return this.like(true, column, val);
    }

    /**
     * like 条件
     *
     * @param condition
     * @param column
     * @param val       为空时忽略
     * @return '%值%'
     */
    @Override
    public Update<T> like(boolean condition, String column, Object val) {
        super.like(condition, column, val);
        return this;
    }

    /**
     * like 条件
     *
     * @param column
     * @param val       为空时忽略
     * @return '值%'
     */
    @Override
    public Update<T> likeLeft(String column, Object val) {
        return this.likeLeft(true, column, val);
    }

    /**
     * like 条件
     *
     * @param condition
     * @param column
     * @param val       为空时忽略
     * @return '值%'
     */
    @Override
    public Update<T> likeLeft(boolean condition, String column, Object val) {
        super.likeRight(condition, column, val);
        return this;
    }

    /**
     * like 条件
     *
     * @param column
     * @param val       为空时忽略
     * @return '%值'
     */
    @Override
    public Update<T> likeRight(String column, Object val) {
        return this.likeRight(true, column, val);
    }

    /**
     * like 条件
     *
     * @param condition
     * @param column
     * @param val       为空时忽略
     * @return '%值'
     */
    @Override
    public Update<T> likeRight(boolean condition, String column, Object val) {
        super.likeLeft(condition, column, val);
        return this;
    }

    /**
     * like 条件
     *
     * @param column
     * @param val       为空时忽略
     * @return
     */
    @Override
    public Update<T> notLike(String column, Object val) {
        return this.notLike(true, column, val);
    }

    /**
     * like 条件
     *
     * @param condition
     * @param column
     * @param val       为空时忽略
     * @return
     */
    @Override
    public Update<T> notLike(boolean condition, String column, Object val) {
        super.notLike(condition, column, val);
        return this;
    }

    /**
     * like 条件
     *
     * @param condition
     * @param keyword
     * @param column
     * @param val       为空时忽略
     * @param sqlLike
     * @return
     */
    @Override
    protected Update<T> likeValue(boolean condition, SqlKeyword keyword, String column, Object val, SqlLike sqlLike) {
        if (condition && condition(val)) {
            super.likeValue(true, keyword, column, val, sqlLike);
        }
        return this;
    }

    /**
     * id in查询
     *
     * @param ids    id列表。为null时忽略；为空时设随机值，让查询无结果
     * @return
     */
    public Update<T> ids(Collection<?> ids) {
        return this.in(true, "id", ids);
    }

    /**
     * in查询
     *
     * @param column
     * @param values    为空时忽略
     * @return
     */
    @Override
    public Update<T> in(String column, Object... values) {
        return this.in(true, column, values);
    }

    /**
     * in查询
     *
     * @param column
     * @param values    为null时忽略；为空时设随机值，让查询无结果
     * @return
     */
    @Override
    public Update<T> in(String column, Collection<?> values) {
        return this.in(true, column, values);
    }

    /**
     * in查询
     *
     * @param condition
     * @param column
     * @param values    为空时忽略
     * @return
     */
    @Override
    public Update<T> in(boolean condition, String column, Object... values) {
        return this.in(condition, column, ArrayUtil.toList(values));
    }

    /**
     * in查询
     *
     * @param condition
     * @param column
     * @param values    为null时忽略；为空时设随机值，让查询无结果
     * @return
     */
    @Override
    public Update<T> in(boolean condition, String column, Collection<?> values) {
        if (condition && values != null) {
            if (values.isEmpty()) {
                // 使用随机数条件，让查询无结果
                super.eq(true, column, System.currentTimeMillis());
            }else{
                // 去重
                values = new HashSet<>(values);
                if (values.size() == 1) {
                    // 单条
                    super.eq(true, column, CollectionUtil.any(values));
                }else{
                    // 多条
                    super.in(true, column, values);
                }
            }
        }
        return this;
    }

    /**
     * id not in查询
     *
     * @param ids   ID列表。为空时忽略
     * @return
     */
    public Update<T> notIds(Collection<?> ids) {
        return this.notIn(true, "id", ids);
    }

    /**
     * not in查询
     *
     * @param column
     * @param values    为空时忽略
     * @return
     */
    @Override
    public Update<T> notIn(String column, Object... values) {
        return this.notIn(true, column, values);
    }

    /**
     * not in查询
     *
     * @param column
     * @param values    为空时忽略
     * @return
     */
    @Override
    public Update<T> notIn(String column, Collection<?> values) {
        return this.notIn(true, column, values);
    }

    /**
     * not in查询
     *
     * @param condition
     * @param column
     * @param values    为空时忽略
     * @return
     */
    @Override
    public Update<T> notIn(boolean condition, String column, Object... values) {
        if (condition && values != null && values.length > 0) {
            super.notIn(true, column, values);
        }
        return this;
    }

    /**
     * not in查询
     *
     * @param condition
     * @param column
     * @param values    为空时忽略
     * @return
     */
    @Override
    public Update<T> notIn(boolean condition, String column, Collection<?> values) {
        if (condition && values != null && values.size() > 0) {
            super.notIn(true, column, values);
        }
        return this;
    }

    /**
     * Exists
     *
     * @param sql       SQL语句，可以通过{0},{1}来占位
     * @param values    值，索引对应sql中的{0},{1}占位符
     * @return
     */
    @Override
    public Update<T> exists(String sql, Object... values) {
        return this.exists(true, sql, values);
    }

    /**
     * Exists
     *
     * @param condition
     * @param sql       SQL语句，可以通过{0},{1}来占位
     * @param values    值，索引对应sql中的{0},{1}占位符
     * @return
     */
    @Override
    public Update<T> exists(boolean condition, String sql, Object... values) {
        super.exists(condition, sql, values);
        return this;
    }

    /**
     * Not Exists
     *
     * @param sql       SQL语句，可以通过{0},{1}来占位
     * @param values    值，索引对应sql中的{0},{1}占位符
     * @return
     */
    @Override
    public Update<T> notExists(String sql, Object... values) {
        return this.notExists(true, sql, values);
    }

    /**
     * Not Exists
     *
     * @param condition
     * @param sql        SQL语句，可以通过{0},{1}来占位
     * @param values    值，索引对应sql中的{0},{1}占位符
     * @return
     */
    @Override
    public Update<T> notExists(boolean condition, String sql, Object... values) {
        super.notExists(condition, sql, values);
        return this;
    }

    /**
     * 大于
     *
     * @param column
     * @param val
     * @return
     */
    @Override
    public Update<T> gt(String column, Object val) {
        return this.gt(true, column, val);
    }

    /**
     * 大于
     *
     * @param condition
     * @param column
     * @param val
     * @return
     */
    @Override
    public Update<T> gt(boolean condition, String column, Object val) {
        super.gt(condition, column, val);
        return this;
    }

    /**
     * 大于等于
     *
     * @param column
     * @param val
     * @return
     */
    @Override
    public Update<T> ge(String column, Object val) {
        return this.ge(true, column, val);
    }

    /**
     * 大于等于
     *
     * @param condition
     * @param column
     * @param val
     * @return
     */
    @Override
    public Update<T> ge(boolean condition, String column, Object val) {
        super.ge(condition, column, val);
        return this;
    }

    /**
     * 小于
     *
     * @param column
     * @param val
     * @return
     */
    @Override
    public Update<T> lt(String column, Object val) {
        return this.lt(true, column, val);
    }

    /**
     * 小于
     *
     * @param condition
     * @param column
     * @param val
     * @return
     */
    @Override
    public Update<T> lt(boolean condition, String column, Object val) {
        super.lt(condition, column, val);
        return this;
    }

    /**
     * 小于等于
     *
     * @param column
     * @param val
     * @return
     */
    @Override
    public Update<T> le(String column, Object val) {
        return this.le(true, column, val);
    }

    /**
     * 小于等于
     *
     * @param condition
     * @param column
     * @param val
     * @return
     */
    @Override
    public Update<T> le(boolean condition, String column, Object val) {
        super.le(condition, column, val);
        return this;
    }

    /**
     * 区间
     *
     * @param column
     * @param val1
     * @param val2
     * @return
     */
    @Override
    public Update<T> between(String column, Object val1, Object val2) {
        return this.between(true, column, val1, val2);
    }

    /**
     * 区间
     *
     * @param condition
     * @param column
     * @param val1
     * @param val2
     * @return
     */
    @Override
    public Update<T> between(boolean condition, String column, Object val1, Object val2) {
        if (condition(val1) && condition(val2)) {
            super.between(condition, column, val1, val2);
        }else if (condition(val1) && !condition(val2)) {
            super.ge(condition, column, val1);
        }else if(!condition(val1) && condition(val2)){
            super.lt(condition, column, val2);
        }
        return this;
    }

    /**
     * 非区间
     *
     * @param column
     * @param val1
     * @param val2
     * @return
     */
    @Override
    public Update<T> notBetween(String column, Object val1, Object val2) {
        return this.notBetween(true, column, val1, val2);
    }

    /**
     * 非区间
     *
     * @param condition
     * @param column
     * @param val1
     * @param val2
     * @return
     */
    @Override
    public Update<T> notBetween(boolean condition, String column, Object val1, Object val2) {
        if (condition(val1) && condition(val2)) {
            super.notBetween(condition, column, val1, val2);
        }else if (condition(val1) && !condition(val2)) {
            super.lt(condition, column, val1);
        }else if(!condition(val1) && condition(val2)){
            super.ge(condition, column, val2);
        }
        return this;
    }

    /**
     * 是null
     *
     * @param column
     * @return
     */
    @Override
    public Update<T> isNull(String column) {
        return this.isNull(true, column);
    }

    /**
     * 是null
     *
     * @param condition
     * @param column
     * @return
     */
    @Override
    public Update<T> isNull(boolean condition, String column) {
        super.isNull(condition, column);
        return this;
    }

    /**
     * 非null
     *
     * @param column
     * @return
     */
    @Override
    public Update<T> isNotNull(String column) {
        return this.isNotNull(true, column);
    }

    /**
     * 非null
     *
     * @param condition
     * @param column
     * @return
     */
    @Override
    public Update<T> isNotNull(boolean condition, String column) {
        super.isNotNull(condition, column);
        return this;
    }

    /**
     * 是''
     *
     * @param column
     * @return
     */
    public Update<T> isEmpty(String column) {
        return this.isEmpty(true, column);
    }

    /**
     * 是''
     *
     * @param condition
     * @param column
     * @return
     */
    public Update<T> isEmpty(boolean condition, String column) {
        super.addCondition(condition, column, EQ, StringConstant.EMPTY);
        return this;
    }

    /**
     * 非''
     *
     * @param column
     * @return
     */
    public Update<T> isNotEmpty(String column) {
        return this.isNotEmpty(true, column);
    }

    /**
     * 非''
     *
     * @param condition
     * @param column
     * @return
     */
    public Update<T> isNotEmpty(boolean condition, String column) {
        super.addCondition(condition, column, NE, StringConstant.EMPTY);
        return this;
    }

    /**
     * 是null或''
     *
     * @param column
     * @return
     */
    public Update<T> isNullEmpty(String column) {
        return this.isNullEmpty(true, column);
    }

    /**
     * 是null或''
     *
     * @param condition
     * @param column
     * @return
     */
    public Update<T> isNullEmpty(boolean condition, String column) {
        return this.andPlus(condition, q -> q.isNull(column).or().isEmpty(column));
    }

    /**
     * 非null或''
     *
     * @param column
     * @return
     */
    public Update<T> isNotNullEmpty(String column) {
        return this.isNotNullEmpty(true, column);
    }

    /**
     * 非null或''
     *
     * @param condition
     * @param column
     * @return
     */
    public Update<T> isNotNullEmpty(boolean condition, String column) {
        if (condition) {
            return this.isNotNull(column).isNotEmpty(column);
        }
        return this;
    }

    /**
     * 条件 - create_time
     *
     * @param createTime
     * @return
     */
    public Update<T> createTime(CreateTime createTime){
        return this.between("create_time", createTime.getStartCreateTime(), createTime.getEndCreateTime());
    }

    /**
     * 条件 - update_time
     *
     * @param updateTime
     * @return
     */
    public Update<T> updateTime(UpdateTime updateTime){
        return this.between("update_time", updateTime.getStartUpdateTime(), updateTime.getEndUpdateTime());
    }

    /**
     * 默认构造
     */
    public Update() {
        super();
    }

    /**
     * 私有构造，只用于生产嵌套 sql
     */
    private Update(T entity, Class<T> entityClass, AtomicInteger paramNameSeq, Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias, SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
        super.setEntity(entity);
        super.setEntityClass(entityClass);
        this.paramNameSeq = paramNameSeq;
        this.paramNameValuePairs = paramNameValuePairs;
        this.expression = mergeSegments;
        this.paramAlias = paramAlias;
        this.lastSql = lastSql;
        this.sqlComment = sqlComment;
        this.sqlFirst = sqlFirst;
    }

    /**
     * 生成嵌套 sql
     */
    protected Update<T> instancePlus() {
        Update<T> update = new Update<>(getEntity(), getEntityClass(), paramNameSeq, paramNameValuePairs, new MergeSegments(),paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
        update.supportHumpParam = this.supportHumpParam;
        return update;
    }

    /**
     * 多重嵌套查询条件
     */
    public Update<T> addNestedConditionPlus(boolean condition, Consumer<Update<T>> consumer) {
        maybeDo(condition, () -> {
            final  Update<T> instance = instancePlus();
            consumer.accept(instance);
            appendSqlSegments(APPLY, instance);
        });
        return this;
    }

    /**
     * and
     *
     * @param condition
     * @return
     */
    @Override
    public Update<T> and(boolean condition) {
        super.and(condition);
        return this;
    }

    /**
     * and
     *
     * @param consumer
     * @return
     */
    public Update<T> andPlus(Consumer<Update<T>> consumer) {
        return this.andPlus(true, consumer);
    }

    /**
     * and
     *
     * @param condition
     * @param consumer
     * @return
     */
    public Update<T> andPlus(boolean condition, Consumer<Update<T>> consumer) {
        return this.and(condition).addNestedConditionPlus(condition, consumer);
    }

    /**
     * or
     *
     * @return
     */
    @Override
    public Update<T> or() {
        return this.or(true);
    }

    /**
     * or
     *
     * @param condition
     * @return
     */
    @Override
    public Update<T> or(boolean condition) {
        super.or(condition);
        return this;
    }

    /**
     * or
     *
     * @param consumer
     * @return
     */
    public Update<T> orPlus(Consumer<Update<T>> consumer) {
        return this.orPlus(true, consumer);
    }

    /**
     * or
     *
     * @param condition
     * @param consumer
     * @return
     */
    public Update<T> orPlus(boolean condition, Consumer<Update<T>> consumer) {
        return this.or(condition).addNestedConditionPlus(condition, consumer);
    }


    /**
     * 条件处理
     *
     * @param val   如果为null或''
     * @return
     */
    private static boolean condition(Object val){
        if (val == null) {
            return false;
        }
        return !(val instanceof String) || !((String) val).isEmpty();
    }

    /**
     * 参数名处理
     *
     * @param column
     *
     * @return 如果supportHumpParam，将xxXX 转 xx_xx
     */
    @Override
    protected String columnToString(String column) {
        if (this.supportHumpParam) {
            return StringUtil.parseHumpToLine(column);
        }
        return column;
    }
}
